//
// Created by xd on 2020/4/14.
//
extern "C" {
#include "avilib/avilib.h"
}

#include "cn_study_aviplayer_OpenGLPlayerActivity.h"
#include "Common.h"
#include <GLES2/gl2.h>
#include <GLES2/gl2ext.h>
#include <malloc.h>

#define POSITION_COMPONENT_COUNT 2
#define BYTES_PER_FLOAT 4
#define TEXTURE_COORDINATES_COMPONENT_COUNT 2
#define STRIDE ((POSITION_COMPONENT_COUNT + TEXTURE_COORDINATES_COMPONENT_COUNT)*BYTES_PER_FLOAT)

GLuint LoadShader(GLenum type, const char *shaderSrc);

static GLuint gProgramObject;

struct Instance {
    char *buffer;
    GLuint texture;

    Instance() : buffer(0), texture(0) {}
};


char vShaderStr[] = "attribute vec4 a_Position;           \n"
                    "attribute vec2 a_TextureCoordinates;               \n"
                    "varying vec2 v_TextureCoordinates;                 \n"
                    "void main()                                        \n"
                    "{                                                  \n"
                    "    v_TextureCoordinates = a_TextureCoordinates;   \n"
                    "    gl_Position = a_Position;                      \n"
                    "}                                                  \n";

char fShaderStr[] =
        "precision mediump float; \n"
        "uniform sampler2D u_TextureUnit;                   \n"
        "varying vec2 v_TextureCoordinates;                 \n"
        "void main()                                        \n"
        "{                                                  \n"
        "    gl_FragColor = texture2D(u_TextureUnit, v_TextureCoordinates);  \n"
        "}                                                  \n";

extern "C"
JNIEXPORT jlong JNICALL
Java_cn_study_aviplayer_OpenGLPlayerActivity_init(JNIEnv *env, jclass clazz, jlong avi) {

    Instance *instance = 0;
    long frameSize = AVI_frame_size((avi_t *) avi, 0);

    if (0 >= frameSize) {

        ThrowException(env, "java/io/RuntimeException", "没有获取到帧大小");
        goto exit;
    }

    instance = new Instance();
    instance->buffer = (char *) malloc(frameSize);
    if (0 == instance->buffer) {

        ThrowException(env, "java/io/RuntimeException", "申请内存失败");
        delete instance;
        instance = 0;
    }

    exit:
    return (jlong) instance;
}


extern "C"
JNIEXPORT void JNICALL
Java_cn_study_aviplayer_OpenGLPlayerActivity_initSurface(JNIEnv *env, jclass clazz, jlong inst,
                                                         jlong avi) {

    Instance *instance = (Instance *) inst;

    GLuint vertexShader;
    GLuint fragmentShader;
    GLuint programObject;
    GLint linked;

    vertexShader = LoadShader(GL_VERTEX_SHADER, vShaderStr);
    fragmentShader = LoadShader(GL_FRAGMENT_SHADER, fShaderStr);

    programObject = glCreateProgram();

    if (programObject == 0) {
        return;
    }
    //附着色器
    glAttachShader(programObject, vertexShader);
    glAttachShader(programObject, fragmentShader);
    // 连接program
    glLinkProgram(programObject);


    // 检查链接状态
    glGetProgramiv(programObject, GL_LINK_STATUS, &linked);

    if (!linked) {
        GLint infoLen = 0;

        //日志长度
        glGetProgramiv(programObject, GL_INFO_LOG_LENGTH, &infoLen);

        if (infoLen > 1) {
            char *infoLog = (char *) malloc(sizeof(char) * infoLen);

            glGetProgramInfoLog(programObject, infoLen, NULL, infoLog);
//            esLogMessage("Error linking program:\n%s\n", infoLog);

            free(infoLog);
        }

        glDeleteProgram(programObject);
        return;
    }

    // 全局
    gProgramObject = programObject;

    glClearColor(1.0f, 1.0f, 1.0f, 0.0f);
    //生成一个纹理对象
    glGenTextures(1, &instance->texture);
    //绑定到生成的纹理上
    glBindTexture(GL_TEXTURE_2D, instance->texture);

    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
}

extern "C"
JNIEXPORT jboolean JNICALL
Java_cn_study_aviplayer_OpenGLPlayerActivity_render(JNIEnv *env, jclass clazz, jlong inst,
                                                    jlong avi) {

    Instance *instance = (Instance *) inst;
    jboolean isFrameRead = JNI_FALSE;

    GLint uTextureUnitLocation;
    GLint aPositionLocation;
    GLint aTextureCoordinatesLocation;
    GLfloat VERTEX_DATA[] = {0.0f, 0.0f, 0.5f, 0.5f, -1.0f, -1.0f, 0.0f, 1.0f,
                             1.0f, -1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 0.0f, -1.0f, 1.0f, 0.0f,
                             0.0f, -1.0f, -1.0f, 0.0f, 1.0f};
    int x, y;
    int vWidth, vHeight;
    float scale = 1.8;

    int keyFrame = 0;

    long frameSize = AVI_read_frame((avi_t *) avi, instance->buffer, &keyFrame);

    if (0 >= frameSize) {

        goto exit;
    }

    isFrameRead = JNI_TRUE;


    vWidth = AVI_video_width((avi_t *) avi) * scale;
    vHeight = AVI_video_height((avi_t *) avi) * scale;

    x = (1080 - vWidth) / 2;
    y = 1920 / 2 - vHeight / 2;

    //设置视频位置及大小
    glViewport(x, y, vWidth, vHeight);
    glClear(GL_COLOR_BUFFER_BIT);
    glUseProgram(gProgramObject);

    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, AVI_video_width((avi_t *) avi),
                 AVI_video_height((avi_t *) avi), 0, GL_RGB,
                 GL_UNSIGNED_SHORT_5_6_5, instance->buffer);

//    uTextureUnitLocation = glGetUniformLocation(gProgramObject, "u_TextureUnit");
//    setUniforms(uTextureUnitLocation, instance->texture);

    //返回属性变量
    aPositionLocation = glGetAttribLocation(gProgramObject, "a_Position");
    aTextureCoordinatesLocation = glGetAttribLocation(gProgramObject, "a_TextureCoordinates");

    //定义通用顶点属性数据数组
    glVertexAttribPointer((GLuint) aPositionLocation, POSITION_COMPONENT_COUNT, GL_FLOAT,
                          0, STRIDE, VERTEX_DATA);
    //启用或禁用通用顶点属性数组
    glEnableVertexAttribArray((GLuint) aPositionLocation);

    glVertexAttribPointer((GLuint) aTextureCoordinatesLocation, POSITION_COMPONENT_COUNT,
                          GL_FLOAT, 0, STRIDE, &VERTEX_DATA[POSITION_COMPONENT_COUNT]);
    glEnableVertexAttribArray((GLuint) aTextureCoordinatesLocation);
    glDrawArrays(GL_TRIANGLE_FAN, 0, 6);

    exit:
    return isFrameRead;
}


extern "C"
JNIEXPORT void JNICALL
Java_cn_study_aviplayer_OpenGLPlayerActivity_free(JNIEnv *env, jclass clazz, jlong inst) {

    Instance *instance = (Instance *) inst;
    if (0 != instance) {

        free(instance->buffer);
        delete instance;
    }
}


/**
 * 装载着色器
 * @param type
 * @param shaderSrc
 * @return
 */
GLuint LoadShader(GLenum type, const char *shaderSrc) {
    GLuint shader;
    GLint compiled;

    // Create the shader object
    shader = glCreateShader(type);

    if (shader == 0) {
        return 0;
    }

    // Load the shader source
    glShaderSource(shader, 1, &shaderSrc, NULL);

    // Compile the shader
    glCompileShader(shader);

    // Check the compile status
    glGetShaderiv(shader, GL_COMPILE_STATUS, &compiled);

    if (!compiled) {
        GLint infoLen = 0;

        glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &infoLen);

        if (infoLen > 1) {
            char *infoLog = (char *) malloc(sizeof(char) * infoLen);

            glGetShaderInfoLog(shader, infoLen, NULL, infoLog);
//            esLogMessage("Error compiling shader:\n%s\n", infoLog);

            free(infoLog);
        }

        glDeleteShader(shader);
        return 0;
    }

    return shader;

}


void setUniforms(int uTextureUnitLocation, int textureId) {
    // Pass the matrix into the shader program.
    //glUniformMatrix4fv(uMatrixLocation, 1, false, matrix);

    // Set the active texture unit to texture unit 0.
    glActiveTexture(GL_TEXTURE0);

    // Bind the texture to this unit.
    glBindTexture(GL_TEXTURE_2D, textureId);

    // Tell the texture uniform sampler to use this texture in the shader by
    // telling it to read from texture unit 0.
    glUniform1i(uTextureUnitLocation, 0);
}